#pragma once

#include <cmath>
#include <entt/entt.hpp>
#include <glm/glm.hpp>
#include "ly/types.hpp"
#include "ly/vec2.hpp"

namespace ly {
template <class C>
class default_trans {};

template <class C>
class unit_trans : public default_trans<C> {
 public:
  glm::mat2 to_mat2() const { return glm::mat2(1.0, 0.0, 0.0, 1.0); }
  glm::mat3 to_mat3() const { return glm::mat3(1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0); }
};

// 旋转+镜像
template <class C>
class fixpoint_trans : public default_trans<C> {
 public:
  typedef ly::point<C> point_t;

  // enum rm_type {
  //   r0 = 0,    //  No rotation
  //   r90 = 1,   //  Rotation by 90 degree counterclockwise
  //   r180 = 2,  //  Rotation by 180 degree counterclockwise
  //   r270 = 3,  //  Rotation by 270 degree counterclockwise
  //   m0 = 4,    //  Mirroring at x-axis
  //   m45 = 5,   //  Mirroring at 45-degree axis
  //   m90 = 6,   //  Mirroring at y-axis
  //   m135 = 7   //  Mirroring at 135-degree axis
  // };

  fixpoint_trans() {}
  fixpoint_trans(int rot, bool mirrx) : m_f((rot & 3) + (mirrx ? 4 : 0)) {}

  fixpoint_trans& operator*=(const fixpoint_trans& t) {
    m_f = ((m_f + (1 - ((m_f & 4) >> 1)) * t.m_f) & 3) + ((m_f ^ t.m_f) & 4);
    return *this;
  }

  bool operator<(const fixpoint_trans& t) const { return m_f < t.m_f; }

  bool operator==(const fixpoint_trans& t) const { return m_f == t.m_f; }

  bool operator!=(const fixpoint_trans& t) const { return !operator==(t); }

  /**
   *  @brief The transformation of a point 
   *
   *  The operator() method transforms the given point.
   *  q = t(p)
   *  
   *  @param p The point to transform
   *  @return The transformed point
   */
  point_t operator()(const point_t& p) const {
    switch (m_f) {
      default:
        return point_t(p.x(), p.y());
      case 1:
        return point_t(-p.y(), p.x());
      case 2:
        return point_t(-p.x(), -p.y());
      case 3:
        return point_t(p.y(), -p.x());
      case 4:
        return point_t(p.x(), -p.y());
      case 5:
        return point_t(p.y(), p.x());
      case 6:
        return point_t(-p.x(), p.y());
      case 7:
        return point_t(-p.y(), -p.x());
    }
  }

  /**
   *  @brief The transformation of a vector 
   *
   *  The operator() method transforms the given vector.
   *  q = t(p)
   *  
   *  @param p The point to transform
   *  @return The transformed point
   */
  std::vector<C> operator()(const std::vector<C>& p) const {
    switch (m_f) {
      default:
        return std::vector<C>(p.x(), p.y());
      case 1:
        return std::vector<C>(-p.y(), p.x());
      case 2:
        return std::vector<C>(-p.x(), -p.y());
      case 3:
        return std::vector<C>(p.y(), -p.x());
      case 4:
        return std::vector<C>(p.x(), -p.y());
      case 5:
        return std::vector<C>(p.y(), p.x());
      case 6:
        return std::vector<C>(-p.x(), p.y());
      case 7:
        return std::vector<C>(-p.y(), -p.x());
    }
  }

  /**
   * @brief 转换为 2x2 变换矩阵
   * @return 2x2 变换矩阵
   */
  glm::mat2 to_mat2() const {
    point_t tx = operator()(point_t(1, 0));
    point_t ty = operator()(point_t(0, 1));
    return glm::mat2(tx.x(), ty.x(), tx.y(), ty.y());
  }

  /**
   * @brief 转换为 3x3 变换矩阵
   * @return 3x3 变换矩阵
   */
  glm::mat3 to_mat3() const { return glm::mat3(1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0); }

  /**
   * @brief 获取完整的变换编码
   * @return 变换编码 (0-7 分别代表 r0, r90, r180, r270, m0, m45, m90, m135)
   */
  int rot() const { return m_f; }

  /**
   * @brief 获取旋转角度值（以度为单位）
   * @return 旋转角度 (0, 90, 180, 或 270)
   */
  int rotation() const {
    return (m_f & 3) * 90;  // 将0-3的编码转换为0/90/180/270度
  }

  /**
   *  @brief 获取角度（以90度为单位）
   *  @return 角度编码值
   */
  int angle() const { return m_f & 3; }

  /**
   * @brief 检查是否应用了镜像变换
   * @return true 如果应用了镜像，false 否则
   */
  bool is_mirror() const { return m_f >= 4; }

  /**
   *  @brief 旋转/镜像编码
   */
  static const int r0 = 0;    //  无旋转
  static const int r90 = 1;   //  逆时针旋转90度
  static const int r180 = 2;  //  逆时针旋转180度
  static const int r270 = 3;  //  逆时针旋转270度
  static const int m0 = 4;    //  沿x轴镜像
  static const int m45 = 5;   //  沿45度轴镜像
  static const int m90 = 6;   //  沿y轴镜像
  static const int m135 = 7;  //  沿135度轴镜像

 private:
  int m_f;
};

// 平移+旋转(90°倍数)+镜像
template <class C>
class simple_trans : public fixpoint_trans<C> {
 public:
  typedef ly::point<C> point_t;
  simple_trans() {}
  simple_trans(int rot, bool mirrx, const point_t& u) : fixpoint_trans<C>(rot, mirrx), m_u(u) {}
  glm::mat2 to_mat2() const { return glm::mat2(1.0, 0.0, 0.0, 1.0); }
  glm::mat3 to_mat3() const { return glm::mat3(1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0); }

 private:
  point_t m_u;
};

//  缩放 + 平移 + 旋转 + 镜像
template <class C, class R>
class cplx_trans {
 public:
  typedef ly::point<R> point_t;
  cplx_trans() : m_sin(0.0), m_cos(1.0), m_mag(1.0) {}
  cplx_trans(const point_t& orig, double mag, double rot, bool mirrx) {
    m_u = orig;
    m_mag = mirrx ? -mag : mag;
    rot *= M_PI / 180.0;
    m_sin = sin(rot);
    m_cos = cos(rot);
  }

  /**
   *  @brief Conversion to a 2d matrix
   */
  glm::mat2 to_mat2() const { return glm::mat2(m_cos * fabs(m_mag), -m_sin * m_mag, m_sin * fabs(m_mag), m_cos * m_mag); }

  /**
   *  @brief Conversion to a 3d matrix
   */
  glm::mat3 to_mat3d() const {
    return glm::mat3(m_cos * fabs(m_mag), -m_sin * m_mag, m_sin * fabs(m_mag), m_cos * m_mag, m_u.x(), m_u.y(), 0.0, 0.0);
  }

  /** 
   *  @brief Accessor to the fp_trans
   */
  const fixpoint_trans<C>& fp_trans() const { return *this; }

  void angle() { return m_mag; }
  void angle(double rot) {
    rot *= M_PI / 180.0;
    m_sin = std::sin(rot);
    m_cos = std::cos(rot);
  }

  double msin() { return m_sin; }
  double mcos() { return m_cos; }
  double mag() { return m_mag; }

  void mirror(bool m) { m_mag = m ? -fabs(m_mag) : fabs(m_mag); }

 private:
  point_t m_u;
  R m_sin, m_cos;
  R m_mag;
};

typedef unit_trans<Coord> UintTrans;
typedef unit_trans<DCoord> DUintTrans;

typedef fixpoint_trans<Coord> FTrans;
typedef fixpoint_trans<DCoord> DFTrans;

typedef simple_trans<Coord> Trans;

typedef simple_trans<DCoord> DTrans;

typedef cplx_trans<Coord, Coord> ICplxTrans;

typedef cplx_trans<Coord, DCoord> CplxTrans;

}  // namespace ly
